package pers.yurwisher.rubick.oauth2.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import pers.yurwisher.rubick.common.enums.tip.OAuth2CustomTipEnum;
import pers.yurwisher.rubick.common.utils.PudgeUtil;
import pers.yurwisher.rubick.common.wrapper.R;

/**
 * 资源服务配置 启用资源服务 启用方法级权限控制
 *
 * @author yq 2021年4月17日 09:50:13
 **/
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Slf4j
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Value("${spring.application.name}")
    private String applicationName;

    /**
     * 配置资源接口安全，http.authorizeRequests()针对的所有url，但是由于登录页面url包含在其中，这里配置会进行token校验，校验不通过返回错误json，
     * 而授权码模式获取code时需要重定向登录页面，重定向过程并不能携带token，所有不能用http.authorizeRequests()，
     * 而是用requestMatchers().antMatchers("")，这里配置的是需要资源接口拦截的url数组
     *
     * @param http
     * @return void
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                // 配置需要保护的资源接口
                .requestMatchers().antMatchers("/oauth/user", "/oauth/logout")
                .and().authorizeRequests().anyRequest().authenticated();
        ;
    }

    /**
     * 这个是跟服务绑定的，注意要跟client配置一致，如果客户端没有，则不能访问
     *
     * @param resources
     * @throws Exception
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources
                // token验证失败处理
                .authenticationEntryPoint((request, response, authException) -> {
                    String message = authException.getMessage();
                    R r;
                    // client中的resourceIds不包含当前服务的resourceId也会走这里
                    if (message != null && message.contains("resource id")) {
                        r = R.fail(OAuth2CustomTipEnum.ACCESS_DENIED);
                    } else {
                        r = R.fail(OAuth2CustomTipEnum.CREDENTIALS_INVALID);
                    }
                    PudgeUtil.responseJSON(response, r);
                })
                //权限不足处理
                .accessDeniedHandler((request, response, accessDeniedException) ->
                        PudgeUtil.responseJSON(response, R.fail(OAuth2CustomTipEnum.ACCESS_DENIED))
                );
        resources.resourceId(applicationName).stateless(true);
    }


}
